#include "xsane.h"
#include "xsane-device-options-window.h"
#include "xsane-front-gtk.h"
#include "xsane-back-gtk.h"

/* ---------------------------------------------------------------------------------------------------------------------- */

gint xsane_device_options_win_delete(GtkWidget *widget, gpointer data)
{
  DBG(DBG_proc, "xsane_device_options_win_delete\n");

  if (preferences.show_device_options)
  {
    xsane_window_get_position(xsane.device_options.window, &xsane.device_options.window_posx, &xsane.device_options.window_posy);
    gtk_window_move(GTK_WINDOW(xsane.device_options.window), xsane.device_options.window_posx, xsane.device_options.window_posy);
  }

  gtk_widget_hide(widget);
  preferences.show_device_options = FALSE;
  gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(xsane.show_device_options_widget), preferences.show_device_options);
  return TRUE;
}

/* ---------------------------------------------------------------------------------------------------------------------- */

void xsane_create_device_options_window()
{
  char windowname[TEXTBUFSIZE];
  GtkWidget *xsane_vbox_device_options;

  xsane.device_options.window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  gtk_window_move(GTK_WINDOW(xsane.device_options.window), XSANE_DEVICE_OPTIONS_WINDOW_POS_X, XSANE_DEVICE_OPTIONS_WINDOW_POS_Y);
  sprintf(windowname, "%s %s", WINDOW_DEVICE_OPTIONS, xsane.device_text);
  gtk_window_set_title(GTK_WINDOW(xsane.device_options.window), (char *) windowname);

  gtk_window_set_resizable(GTK_WINDOW(xsane.device_options.window), FALSE);
  g_signal_connect(GTK_OBJECT(xsane.device_options.window), "delete_event", GTK_SIGNAL_FUNC(xsane_device_options_win_delete), NULL);

  xsane_set_window_icon(xsane.device_options.window, 0);
  gtk_window_add_accel_group(GTK_WINDOW(xsane.device_options.window), xsane.accelerator_group); 

  xsane_vbox_device_options = gtk_vbox_new(FALSE, 5); /* has been TRUE before I added backend pixmap */
  gtk_container_set_border_width(GTK_CONTAINER(xsane_vbox_device_options), 5);
  gtk_container_add(GTK_CONTAINER(xsane.device_options.window), xsane_vbox_device_options);
  gtk_widget_show(xsane_vbox_device_options);

  /* add vendor`s logo */
  xsane_vendor_pixmap_new(xsane.device_options.window->window, xsane_vbox_device_options);

  /* create  a subwindow so the device options dialog keeps its position on rebuilds: */
  xsane.device_options.window_body = gtk_hbox_new(/* homogeneous */ FALSE, 0);
  gtk_box_pack_start(GTK_BOX(xsane_vbox_device_options), xsane.device_options.window_body, TRUE, TRUE, 0);
  gtk_widget_show(xsane.device_options.window_body);

  xsane_build_device_options();
}

/* ---------------------------------------------------------------------------------------------------------------------- */

void xsane_update_well_known_options()
{
  const SANE_Option_Descriptor *opt;
  SANE_Handle dev = xsane.device_options.dev;
  int opt_num;

  /* reset well-known options: */
  xsane.device_options.well_known.scanmode        = -1;
  xsane.device_options.well_known.scansource      = -1;
  xsane.device_options.well_known.preview         = -1;
  xsane.device_options.well_known.dpi             = -1;
  xsane.device_options.well_known.dpi_x           = -1;
  xsane.device_options.well_known.dpi_y           = -1;
  xsane.device_options.well_known.coord[xsane_back_gtk_TL_X] = -1;
  xsane.device_options.well_known.coord[xsane_back_gtk_TL_Y] = -1;
  xsane.device_options.well_known.coord[xsane_back_gtk_BR_X] = -1;
  xsane.device_options.well_known.coord[xsane_back_gtk_BR_Y] = -1;
  xsane.device_options.well_known.gamma_vector    = -1;
  xsane.device_options.well_known.gamma_vector_r  = -1;
  xsane.device_options.well_known.gamma_vector_g  = -1;
  xsane.device_options.well_known.gamma_vector_b  = -1;
  xsane.device_options.well_known.bit_depth       = -1;
  xsane.device_options.well_known.threshold       = -1;
  xsane.device_options.well_known.shadow          = -1;
  xsane.device_options.well_known.shadow_r        = -1;
  xsane.device_options.well_known.shadow_g        = -1;
  xsane.device_options.well_known.shadow_b        = -1;
  xsane.device_options.well_known.highlight       = -1;
  xsane.device_options.well_known.highlight_r     = -1;
  xsane.device_options.well_known.highlight_g     = -1;
  xsane.device_options.well_known.highlight_b     = -1;
  xsane.device_options.well_known.batch_scan_start     = -1;
  xsane.device_options.well_known.batch_scan_loop      = -1;
  xsane.device_options.well_known.batch_scan_end       = -1;
  xsane.device_options.well_known.batch_scan_next_tl_y = -1;

  for (opt_num = 1; opt_num < xsane.device_options.num_elements; ++opt_num)
  {
    opt = xsane_get_option_descriptor(dev, opt_num);

    if (!SANE_OPTION_IS_ACTIVE(opt->cap))
    {
      continue;
    }

    /* pick up well-known options as we go: */
    if (opt->name)
    {
      if (strcmp(opt->name, SANE_NAME_PREVIEW) == 0 && opt->type == SANE_TYPE_BOOL)
      {
        xsane.device_options.well_known.preview = opt_num;
        continue;
      }
      else if (strcmp(opt->name, SANE_NAME_SCAN_RESOLUTION) == 0
               && opt->unit == SANE_UNIT_DPI && (opt->type == SANE_TYPE_INT || opt->type == SANE_TYPE_FIXED))
        xsane.device_options.well_known.dpi = opt_num;
      else if (strcmp(opt->name, SANE_NAME_SCAN_X_RESOLUTION) == 0
               && opt->unit == SANE_UNIT_DPI && (opt->type == SANE_TYPE_INT || opt->type == SANE_TYPE_FIXED))
        xsane.device_options.well_known.dpi_x = opt_num;
      else if (strcmp(opt->name, SANE_NAME_SCAN_Y_RESOLUTION) == 0
               && opt->unit == SANE_UNIT_DPI && (opt->type == SANE_TYPE_INT || opt->type == SANE_TYPE_FIXED))
        xsane.device_options.well_known.dpi_y = opt_num;
      else if (strcmp (opt->name, SANE_NAME_SCAN_MODE) == 0)
        xsane.device_options.well_known.scanmode = opt_num;
      else if (strcmp (opt->name, SANE_NAME_SCAN_SOURCE) == 0)
        xsane.device_options.well_known.scansource = opt_num;
      else if (strcmp (opt->name, SANE_NAME_SCAN_TL_X) == 0)
        xsane.device_options.well_known.coord[xsane_back_gtk_TL_X] = opt_num;
      else if (strcmp (opt->name, SANE_NAME_SCAN_TL_Y) == 0)
        xsane.device_options.well_known.coord[xsane_back_gtk_TL_Y] = opt_num;
      else if (strcmp (opt->name, SANE_NAME_SCAN_BR_X) == 0)
        xsane.device_options.well_known.coord[xsane_back_gtk_BR_X] = opt_num;
      else if (strcmp (opt->name, SANE_NAME_SCAN_BR_Y) == 0)
        xsane.device_options.well_known.coord[xsane_back_gtk_BR_Y] = opt_num;
      else if (strcmp (opt->name, SANE_NAME_GAMMA_VECTOR) == 0)
        xsane.device_options.well_known.gamma_vector = opt_num;
      else if (strcmp (opt->name, SANE_NAME_GAMMA_VECTOR_R) == 0)
        xsane.device_options.well_known.gamma_vector_r = opt_num;
      else if (strcmp (opt->name, SANE_NAME_GAMMA_VECTOR_G) == 0)
        xsane.device_options.well_known.gamma_vector_g = opt_num;
      else if (strcmp (opt->name, SANE_NAME_GAMMA_VECTOR_B) == 0)
        xsane.device_options.well_known.gamma_vector_b = opt_num;
      else if (strcmp (opt->name, SANE_NAME_BIT_DEPTH) == 0)
        xsane.device_options.well_known.bit_depth = opt_num;
      else if (strcmp (opt->name, SANE_NAME_THRESHOLD) == 0)
        xsane.device_options.well_known.threshold = opt_num;
      else if (strcmp (opt->name, SANE_NAME_HIGHLIGHT) == 0)
        xsane.device_options.well_known.highlight = opt_num;
      else if (strcmp (opt->name, SANE_NAME_HIGHLIGHT_R) == 0)
        xsane.device_options.well_known.highlight_r = opt_num;
      else if (strcmp (opt->name, SANE_NAME_HIGHLIGHT_G) == 0)
        xsane.device_options.well_known.highlight_g = opt_num;
      else if (strcmp (opt->name, SANE_NAME_HIGHLIGHT_B) == 0)
        xsane.device_options.well_known.highlight_b = opt_num;
      else if (strcmp (opt->name, SANE_NAME_SHADOW) == 0)
        xsane.device_options.well_known.shadow = opt_num;
      else if (strcmp (opt->name, SANE_NAME_SHADOW_R) == 0)
        xsane.device_options.well_known.shadow_r = opt_num;
      else if (strcmp (opt->name, SANE_NAME_SHADOW_G) == 0)
        xsane.device_options.well_known.shadow_g = opt_num;
      else if (strcmp (opt->name, SANE_NAME_SHADOW_B) == 0)
        xsane.device_options.well_known.shadow_b = opt_num;
      else if (strcmp (opt->name, SANE_NAME_BATCH_SCAN_START) == 0)
        xsane.device_options.well_known.batch_scan_start = opt_num;
      else if (strcmp (opt->name, SANE_NAME_BATCH_SCAN_LOOP) == 0)
        xsane.device_options.well_known.batch_scan_loop = opt_num;
      else if (strcmp (opt->name, SANE_NAME_BATCH_SCAN_END) == 0)
        xsane.device_options.well_known.batch_scan_end = opt_num;
      else if (strcmp (opt->name, SANE_NAME_BATCH_SCAN_NEXT_TL_Y) == 0)
        xsane.device_options.well_known.batch_scan_next_tl_y = opt_num;
    }
  }
}

gboolean xsane_build_device_option_widget(int opt_num, GtkWidget *parent, GtkWidget *label)
{
  const SANE_Option_Descriptor *opt;
  SANE_Status status;
  SANE_Word quant, val;
  char *buf, str[16], title[TEXTBUFSIZE];
  DialogElement *elem;
  SANE_Int num_words;
  char **str_list;
  int j;
  double dval, dmin, dmax, dquant;
  gboolean fill = TRUE;

  quant = 0;
  val = 0;
  dval = 0;
  dmin = 0;
  dmax = 0;
  dquant = 0;

  opt = xsane_get_option_descriptor(xsane.device_options.dev, opt_num);

  elem = &(xsane.device_options.element[opt_num]);

  if (opt->unit == SANE_UNIT_NONE)
  {
    snprintf(title, sizeof(title), "%s", _BGT(opt->title));
  }
  else
  {
    snprintf(title, sizeof(title), "%s [%s]", _BGT(opt->title), xsane_back_gtk_unit_string(opt->unit));
  }

  switch (opt->type)
  {
    case SANE_TYPE_GROUP:
      break;

    case SANE_TYPE_BOOL:
      {
        if (!opt->name)
        {
          DBG(DBG_error, "xsane_build_device_option_widget: Option %d, type SANE_TYPE_BOOL\n", opt_num);
          DBG(DBG_error, "=> %s\n", ERR_OPTION_NAME_NULL);
          DBG(DBG_error, "=> %s\n", ERR_BACKEND_BUG);
          return FALSE;
        }

        if ( (strcmp(opt->name, SANE_NAME_BATCH_SCAN_START)  ) && /* do not show batch scan options */
             (strcmp(opt->name, SANE_NAME_BATCH_SCAN_LOOP)  ) && 
             (strcmp(opt->name, SANE_NAME_BATCH_SCAN_END)  ) )
        {
          assert(opt->size == sizeof(SANE_Word));

          // get current value
          if (SANE_OPTION_IS_ACTIVE(opt->cap))
          {
            status = xsane_control_option(xsane.device_options.dev, opt_num, SANE_ACTION_GET_VALUE, &val, 0);
            if (status != SANE_STATUS_GOOD)
            {
              goto get_value_failed;
            }
          }

          // create widget
          xsane_back_gtk_check_button_new(opt_num,
              parent, title,
              xsane.tooltips, _BGT(opt->desc));
          xsane_back_gtk_check_button_refresh(opt_num);
        }
        else
        {
          return FALSE;
        }
      }
      break;

    case SANE_TYPE_INT:
      if (!opt->name)
      {
        DBG(DBG_error, "xsane_build_device_option_widget: Option %d, type SANE_TYPE_INT\n", opt_num);
        DBG(DBG_error, "=> %s\n", ERR_OPTION_NAME_NULL);
        DBG(DBG_error, "=> %s\n", ERR_BACKEND_BUG);
        return FALSE;
      }

      if (opt->size != sizeof(SANE_Word))
      {
        return FALSE;
      }

      // get current value
      if (SANE_OPTION_IS_ACTIVE(opt->cap))
      {
        status = xsane_control_option(xsane.device_options.dev, opt_num, SANE_ACTION_GET_VALUE, &val, 0);
        if (status != SANE_STATUS_GOOD)
        {
          goto get_value_failed;
        }
      }

      switch (opt->constraint_type)
      {
        case SANE_CONSTRAINT_NONE:
          // create widget
          xsane_back_gtk_value_new(opt_num, parent, title, (opt->cap & SANE_CAP_AUTOMATIC), xsane.tooltips, _BGT(opt->desc));
          xsane_back_gtk_value_refresh(opt_num);
          break;

        case SANE_CONSTRAINT_RANGE:
          {
            /* use a scale */
            quant = opt->constraint.range->quant;

            if (quant == 0)
              quant = 1; /* we have integers */

            if (label == NULL)
            {
              label = gtk_label_new(title);
              fill = FALSE;
            }

            // create widget
            xsane_back_gtk_range_new(opt_num, parent, label,
                quant, (opt->cap & SANE_CAP_AUTOMATIC),
                xsane.tooltips, _BGT(opt->desc), fill);
            xsane_back_gtk_range_refresh(opt_num);
          }
          break;

        case SANE_CONSTRAINT_WORD_LIST:
          {
            int current = 0;

            /* use a "list-selection" widget */
            num_words = opt->constraint.word_list[0];
            str_list = (char **)alloca((num_words + 1) * sizeof(str_list[0]));
            for (j = 0; j < num_words; ++j)
            {
              sprintf(str, "%d", opt->constraint.word_list[j + 1]);
              str_list[j] = strdup(str);
              if (opt->constraint.word_list[j + 1] == val)
                current = j;
            }
            str_list[j] = 0;
            sprintf(str, "%d", val);

            if (label == NULL)
            {
              label = gtk_label_new(title);
              fill = FALSE;
            }

            // create widget
            xsane_back_gtk_combo_box_new(opt_num, parent, label, str_list,
                xsane.tooltips, _BGT(opt->desc), fill);
            xsane_back_gtk_combo_box_refresh(opt_num);
          }
          break;

        default:
          DBG(DBG_error, "xsane_build_device_option_widget: Option %d, type SANE_TYPE_INT\n", opt_num);
          DBG(DBG_error, "=> %s %d!\n", ERR_UNKNOWN_CONSTRAINT_TYPE, opt->constraint_type);
          return FALSE;
      }
      break;

    case SANE_TYPE_FIXED:
      if (!opt->name)
      {
        DBG(DBG_error, "xsane_build_device_option_widget: Option %d, type SANE_TYPE_FIXED\n", opt_num);
        DBG(DBG_error, "=> %s\n", ERR_OPTION_NAME_NULL);
        DBG(DBG_error, "=> %s\n", ERR_BACKEND_BUG);
        return FALSE;
      }

      if (opt->size != sizeof (SANE_Word))
      {
        return FALSE;
      }

      // get current value
      if (SANE_OPTION_IS_ACTIVE(opt->cap))
      {
        status = xsane_control_option(xsane.device_options.dev, opt_num, SANE_ACTION_GET_VALUE, &val, 0);
        if (status != SANE_STATUS_GOOD)
        {
          goto get_value_failed;
        }
      }

      switch (opt->constraint_type)
      {
        case SANE_CONSTRAINT_NONE:
          // create widget
          xsane_back_gtk_value_new(opt_num, parent, title, (opt->cap & SANE_CAP_AUTOMATIC), xsane.tooltips, _BGT(opt->desc));
          xsane_back_gtk_value_refresh(opt_num);
          break;

        case SANE_CONSTRAINT_RANGE:
          {
            /* use a scale */
            dquant = SANE_UNFIX(quant = opt->constraint.range->quant);

            if (opt->unit == SANE_UNIT_MM)
              dquant /= preferences.length_unit;

            if (dquant == 0) /* no quantization specified */
              dquant = 0.001; /* display x.3 digits */

            if (label == NULL)
            {
              label = gtk_label_new(title);
              fill = FALSE;
            }

            // create widget
            xsane_back_gtk_range_new(opt_num, parent, label,
                dquant, (opt->cap & SANE_CAP_AUTOMATIC),
                xsane.tooltips, _BGT(opt->desc), fill);
            xsane_back_gtk_range_refresh(opt_num);
          }
          break;

        case SANE_CONSTRAINT_WORD_LIST:
          {
            int current = 0;

            /* use a "list-selection" widget */
            num_words = opt->constraint.word_list[0];
            str_list = (char **)alloca((num_words + 1) * sizeof (str_list[0]));
            for (j = 0; j < num_words; ++j)
            {
              sprintf(str, "%g", SANE_UNFIX(opt->constraint.word_list[j + 1]));
              str_list[j] = strdup(str);
              if (opt->constraint.word_list[j + 1] == val)
                current = j;
            }
            str_list[j] = 0;
            sprintf(str, "%g", SANE_UNFIX(val));

            if (label == NULL)
            {
              label = gtk_label_new(title);
              fill = FALSE;
            }

            // create widget
            xsane_back_gtk_combo_box_new(opt_num, parent, label, str_list,
                xsane.tooltips, _BGT(opt->desc), fill);
            xsane_back_gtk_combo_box_refresh(opt_num);
          }
          break;

        default:
          DBG(DBG_error, "xsane_build_device_option_widget: Option %d, type SANE_TYPE_FIXED\n", opt_num);
          DBG(DBG_error, "=> %s %d!\n", ERR_UNKNOWN_CONSTRAINT_TYPE, opt->constraint_type);
          return FALSE;
      }
      break;

    case SANE_TYPE_STRING:
      if (!opt->name)
      {
        DBG(DBG_error, "xsane_build_device_option_widget: Option %d, type SANE_TYPE_STRING\n", opt_num);
        DBG(DBG_error, "=> %s\n", ERR_OPTION_NAME_NULL);
        DBG(DBG_error, "=> %s\n", ERR_BACKEND_BUG);
        return FALSE;
      }

      if (!opt->size)
      {
        DBG(DBG_error, "xsane_build_device_option_widget: Option %d, type SANE_TYPE_STRING\n", opt_num);
        DBG(DBG_error, "=> %s\n", ERR_OPTION_ZERO_SIZE);
        DBG(DBG_error, "=> %s\n", ERR_BACKEND_BUG);
        return FALSE;
      }

      // get current value
      buf = (char *)alloca(opt->size);
      if (SANE_OPTION_IS_ACTIVE(opt->cap))
      {
        status = xsane_control_option(xsane.device_options.dev, opt_num, SANE_ACTION_GET_VALUE, buf, 0);
        if (status != SANE_STATUS_GOOD)
        {
          goto get_value_failed;
        }
      }

      switch (opt->constraint_type)
      {
        case SANE_CONSTRAINT_STRING_LIST:
          {
            int current = 0;
            const char *p = NULL;

            for (j = 0, p = opt->constraint.string_list[0]; p != NULL; p = opt->constraint.string_list[++j])
            {
              if (strcmp(p, buf) == 0)
              {
                current = j;
                break;
              }
            }

            if (label == NULL)
            {
              label = gtk_label_new(title);
              fill = FALSE;
            }

            // create widget
            xsane_back_gtk_combo_box_new(opt_num, parent, label, (char **) opt->constraint.string_list,
                xsane.tooltips, _BGT(opt->desc), fill);
            xsane_back_gtk_combo_box_refresh(opt_num);
          }
          break;

        case SANE_CONSTRAINT_NONE:
          // create widget
          xsane_back_gtk_text_entry_new(opt_num, parent, title,
              xsane.tooltips, _BGT(opt->desc));
          xsane_back_gtk_text_entry_refresh(opt_num);
          break;

        default:
          DBG(DBG_error, "xsane_build_device_option_widget: Option %d, type SANE_TYPE_STRING\n", opt_num);
          DBG(DBG_error, "=> %s %d!\n", ERR_UNKNOWN_CONSTRAINT_TYPE, opt->constraint_type);
          return FALSE;
      }
      break;

    case SANE_TYPE_BUTTON:
      if (!opt->name)
      {
        DBG(DBG_error, "xsane_build_device_option_widget: Option %d, type SANE_TYPE_BUTTON\n", opt_num);
        DBG(DBG_error, "=> %s\n", ERR_OPTION_NAME_NULL);
        DBG(DBG_error, "=> %s\n", ERR_BACKEND_BUG);
        return FALSE;
      }

      // create widget
      xsane_back_gtk_button_new(opt_num, parent, title, xsane.tooltips, _BGT(opt->desc));
      break;

    default:
      DBG(DBG_error, "xsane_build_device_option_widget: %s %d\n", ERR_UNKNOWN_TYPE, opt->type);
      return FALSE;
  }

  return TRUE;

  get_value_failed:
  {
    char msg[TEXTBUFSIZE];

    sprintf(msg, "%s %s: %s.", ERR_GET_OPTION, opt->name, XSANE_STRSTATUS(status));
    xsane_back_gtk_error(msg, TRUE);
    return FALSE;
  }
}

GtkWidget *xsane_build_device_options_box()
{
 GtkWidget *device_options_vbox;
 GtkWidget *options_expander, *options_vbox, *parent;
 GtkWidget *standard_options, *separator, *advanced_options;

 const SANE_Option_Descriptor *opt;
 SANE_Handle dev = xsane.device_options.dev;
 DialogElement *elem;
 int opt_num;

  DBG(DBG_proc, "xsane_build_device_options_box\n");
printf( "  xsane_build_device_options_box\n");

  /* device options */
  device_options_vbox = gtk_vbox_new(FALSE, 2);
  gtk_widget_show(device_options_vbox);
  device_options_vbox = device_options_vbox;

  options_expander = NULL;
  options_vbox = NULL;
  parent = NULL;
  separator = NULL;
  standard_options = device_options_vbox;
  advanced_options = device_options_vbox;

  opt = NULL;

  for (opt_num = 1; opt_num < xsane.device_options.num_elements; ++opt_num)
  {
    opt = xsane_get_option_descriptor(dev, opt_num);

    if ((opt->name != NULL) && (strcmp(opt->name, SANE_NAME_PREVIEW) == 0) && (opt->type == SANE_TYPE_BOOL))
    {
      xsane.device_options.well_known.preview = opt_num;
      continue;
    }

    elem = &(xsane.device_options.element[opt_num]);

    if ((opt->type != SANE_TYPE_GROUP) && (opt->cap & SANE_CAP_ADVANCED))
    {
      parent = advanced_options;
      elem->widget = advanced_options;
    }
    else
    {
      parent = standard_options;
      elem->widget = standard_options;
    }

    if (opt->type == SANE_TYPE_GROUP)
    {
      if (options_expander != NULL)
      {
        if (gtk_container_get_children(GTK_CONTAINER(standard_options)) != NULL)
          gtk_widget_show(standard_options);
        if (gtk_container_get_children(GTK_CONTAINER(advanced_options)) != NULL)
          gtk_widget_show(advanced_options);
        if ((gtk_container_get_children(GTK_CONTAINER(standard_options)) != NULL)
            && (gtk_container_get_children(GTK_CONTAINER(advanced_options)) != NULL))
          gtk_widget_show(separator);
      }

      /* options group: put standard options to the standard options window */
      options_vbox = xsane_back_gtk_expander_new(device_options_vbox, _BGT(opt->title));
      options_expander = options_vbox->parent->parent;
      gtk_widget_hide(options_expander);
      if ((opt->name != NULL) && (stricmp(opt->name, SANE_NAME_SCAN_MODE) == 0))
        gtk_expander_set_expanded(GTK_EXPANDER(options_expander), TRUE);
      elem->widget = standard_options;

      standard_options = gtk_vbox_new(FALSE, 2);
      gtk_box_pack_start(GTK_BOX(options_vbox), standard_options, FALSE, FALSE, 0);

      advanced_options = gtk_vbox_new(FALSE, 2);
      gtk_box_pack_end(GTK_BOX(options_vbox), advanced_options, FALSE, FALSE, 0);

      separator = gtk_hseparator_new();
      gtk_box_pack_end(GTK_BOX(options_vbox), separator, FALSE, FALSE, 0);
    }
    else if (((strcmp(opt->name, SANE_NAME_THRESHOLD) == 0) && (xsane.lineart_mode != XSANE_LINEART_STANDARD))
        || (strcmp (opt->name, SANE_NAME_SCAN_MODE)  == 0)
        || (strcmp (opt->name, SANE_NAME_SCAN_SOURCE)  == 0)
        || (strcmp(opt->name, SANE_NAME_BATCH_SCAN_NEXT_TL_Y) == 0)
        || (strcmp(opt->name, SANE_NAME_BIT_DEPTH) == 0)
        || (strcmp(opt->name, SANE_NAME_SCAN_BR_X) == 0)
        || (strcmp(opt->name, SANE_NAME_SCAN_BR_Y) == 0)
        || (strcmp(opt->name, SANE_NAME_SCAN_RESOLUTION) == 0)
        || (strcmp(opt->name, SANE_NAME_SCAN_TL_X) == 0)
        || (strcmp(opt->name, SANE_NAME_SCAN_TL_Y) == 0)
        || (strcmp(opt->name, SANE_NAME_SCAN_X_RESOLUTION) == 0)
        || (strcmp(opt->name, SANE_NAME_SCAN_Y_RESOLUTION) == 0)
        )
    {
      ;
    }
    else
    {
      if (xsane_build_device_option_widget(opt_num, parent, NULL))
        gtk_widget_show(options_expander);
    }
    continue;
  }

  if (options_expander != NULL)
  {
    if (gtk_container_get_children(GTK_CONTAINER(standard_options)) != NULL)
      gtk_widget_show(standard_options);
    if (gtk_container_get_children(GTK_CONTAINER(advanced_options)) != NULL)
      gtk_widget_show(advanced_options);
    if ((gtk_container_get_children(GTK_CONTAINER(standard_options)) != NULL)
        && (gtk_container_get_children(GTK_CONTAINER(advanced_options)) != NULL))
      gtk_widget_show(separator);
  }

printf( "  done xsane_build_device_options_box\n");
  return device_options_vbox;
}

void xsane_build_device_options()
{
printf("xsane_build_device_options\n");
  xsane_update_well_known_options();

  xsane.device_options.vbox = xsane_build_device_options_box();

  if ((xsane.device_options.well_known.dpi_x == -1) && (xsane.device_options.well_known.dpi_y != -1))
  {
    xsane.device_options.well_known.dpi_x = xsane.device_options.well_known.dpi;
  }

  if (xsane.device_options.window != NULL)
    gtk_container_add(GTK_CONTAINER(xsane.device_options.window_body), xsane.device_options.vbox);
printf("done xsane_build_device_options\n");
}

